#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>

#include "gui/gui.h"

typedef struct ImageViewData
{
    const GUI_Texture *tex;
    int scale_type;
    uint32_t tint_color;
    int tex_ori_w;
    int tex_ori_h;
    int tex_dst_x;
    int tex_dst_y;
    int tex_dst_w;
    int tex_dst_h;
} ImageViewData;

static int ImageViewDestroyData(ImageViewData *data)
{
    if (!data)
        return -1;

    free(data);
    return 0;
}

int ImageViewMeasure(ImageView *imageView)
{
    if (!imageView || !imageView->viewdata)
        return -1;

    ImageViewData *imageData = (ImageViewData *)imageView->viewdata;
    LayoutParams *imageParams = &imageView->params;

    if (imageParams->available_w <= 0 || imageParams->available_h <= 0)
    {
        imageView->measured_w = 0;
        imageView->measured_h = 0;
        return -1;
    }

    int layout_max_w = imageParams->available_w - imageParams->margin_left - imageParams->margin_right;
    int layout_max_h = imageParams->available_h - imageParams->margin_top - imageParams->margin_bottom;
    int layout_wrap_w = imageData->tex_ori_w + imageView->padding_left + imageView->padding_right;
    int layout_wrap_h = imageData->tex_ori_h + imageView->padding_top + imageView->padding_bottom;

    imageView->wrap_w = layout_wrap_w;
    imageView->wrap_h = layout_wrap_h;

    if (imageParams->layout_w == TYPE_LAYOUT_SIZE_MATH_PARENT)
        imageView->measured_w = layout_max_w;
    else if (imageParams->layout_w == TYPE_LAYOUT_SIZE_WRAP_CONTENT)
        imageView->measured_w = layout_wrap_w;
    else
        imageView->measured_w = imageParams->layout_w;
    if (imageView->measured_w > layout_max_w)
        imageView->measured_w = layout_max_w;
    if (imageView->measured_w < 0)
        imageView->measured_w = 0;

    if (imageParams->layout_h == TYPE_LAYOUT_SIZE_MATH_PARENT)
        imageView->measured_h = layout_max_h;
    else if (imageParams->layout_h == TYPE_LAYOUT_SIZE_WRAP_CONTENT)
        imageView->measured_h = layout_wrap_h;
    else
        imageView->measured_h = imageParams->layout_h;
    if (imageView->measured_h > layout_max_h)
        imageView->measured_h = layout_max_h;
    if (imageView->measured_h < 0)
        imageView->measured_h = 0;

    if (imageData->tex)
    {
        const int tex_max_w = imageView->measured_w - imageView->padding_left - imageView->padding_right;
        const int tex_max_h = imageView->measured_h - imageView->padding_top - imageView->padding_bottom;
        const int tex_ori_w = imageData->tex_ori_w;
        const int tex_ori_h = imageData->tex_ori_h;
        const float aspect_ratio = (float)tex_ori_w / tex_ori_h;

        switch (imageData->scale_type)
        {
        case TYPE_IMAGE_SCALE_CENTER: // 原图，居中，（宽高度小于视图：原图）
                                      //            （宽高度大于视图：裁剪）
            imageData->tex_dst_w = tex_ori_w;
            imageData->tex_dst_h = tex_ori_h;
            imageData->tex_dst_x = (tex_max_w - imageData->tex_dst_w) / 2;
            imageData->tex_dst_y = (tex_max_h - imageData->tex_dst_h) / 2;

            break;

        case TYPE_IMAGE_SCALE_CENTER_CROP: // 等比缩放，居中，（宽高度小于视图：等比放大）
                                           //                （宽高度大于视图：裁剪）
            imageData->tex_dst_w = tex_ori_w;
            imageData->tex_dst_h = tex_ori_h;
            if (imageData->tex_dst_w < tex_max_w)
            {
                imageData->tex_dst_w = tex_max_w;
                imageData->tex_dst_h = imageData->tex_dst_w / aspect_ratio;
            }
            if (imageData->tex_dst_h < tex_max_h)
            {
                imageData->tex_dst_h = tex_max_h;
                imageData->tex_dst_w = imageData->tex_dst_h * aspect_ratio;
            }
            imageData->tex_dst_x = (tex_max_w - imageData->tex_dst_w) / 2;
            imageData->tex_dst_y = (tex_max_h - imageData->tex_dst_h) / 2;

            break;

        case TYPE_IMAGE_SCALE_CENTER_INSIDE: // 等比缩放，居中，（宽高度小于视图： 原图）
                                             //                （宽高度大于视图： 等比缩小）
            imageData->tex_dst_w = tex_ori_w;
            imageData->tex_dst_h = tex_ori_h;
            if (imageData->tex_dst_w > tex_max_w)
            {
                imageData->tex_dst_w = tex_max_w;
                imageData->tex_dst_h = imageData->tex_dst_w / aspect_ratio;
            }
            if (imageData->tex_dst_h > tex_max_h)
            {
                imageData->tex_dst_h = tex_max_h;
                imageData->tex_dst_w = imageData->tex_dst_h * aspect_ratio;
            }
            imageData->tex_dst_x = (tex_max_w - imageData->tex_dst_w) / 2;
            imageData->tex_dst_y = (tex_max_h - imageData->tex_dst_h) / 2;

            break;

        case TYPE_IMAGE_SCALE_FIT_XY: // 拉伸缩放，左上对齐 （宽==视图，高==视图）
            imageData->tex_dst_w = tex_max_w;
            imageData->tex_dst_h = tex_max_h;
            imageData->tex_dst_x = 0;
            imageData->tex_dst_y = 0;

            break;

        case TYPE_IMAGE_SCALE_FIT_START: // 等比缩放，左上对齐，（宽==视图，高等比缩放）
            imageData->tex_dst_w = tex_max_w;
            imageData->tex_dst_h = imageData->tex_dst_w / aspect_ratio;
            imageData->tex_dst_x = 0;
            imageData->tex_dst_y = 0;

            break;

        case TYPE_IMAGE_SCALE_FIT_END: // 等比缩放，左下对齐，（宽==视图，高等比缩放）
            imageData->tex_dst_w = tex_max_w;
            imageData->tex_dst_h = imageData->tex_dst_w / aspect_ratio;
            imageData->tex_dst_x = 0;
            imageData->tex_dst_y = tex_max_h - imageData->tex_dst_h;

            break;

        case TYPE_IMAGE_SCALE_FIT_CENTER: // 等比缩放，垂直居中，（宽==视图，高等比缩放）
            imageData->tex_dst_w = tex_max_w;
            imageData->tex_dst_h = imageData->tex_dst_w / aspect_ratio;
            imageData->tex_dst_x = 0;
            imageData->tex_dst_y = (tex_max_h - imageData->tex_dst_h) / 2;

            break;

        case TYPE_IMAGE_SCALE_FIT_CENTER_CROP: // 等比缩放，居中，（(宽==视图，高>=视图) || (高==视图，宽>=视图））
            imageData->tex_dst_w = tex_max_w;
            imageData->tex_dst_h = imageData->tex_dst_w / aspect_ratio;
            if (imageData->tex_dst_h < tex_max_h)
            {
                imageData->tex_dst_h = tex_max_h;
                imageData->tex_dst_w = imageData->tex_dst_h * aspect_ratio;
            }
            imageData->tex_dst_x = (tex_max_w - imageData->tex_dst_w) / 2;
            imageData->tex_dst_y = (tex_max_h - imageData->tex_dst_h) / 2;

            break;

        case TYPE_IMAGE_SCALE_FIT_CENTER_INSIDE: // 等比缩放，居中，（(宽==视图，高<=视图) || (高==视图，宽<=视图））
            imageData->tex_dst_w = tex_max_w;
            imageData->tex_dst_h = imageData->tex_dst_w / aspect_ratio;
            if (imageData->tex_dst_h > tex_max_h)
            {
                imageData->tex_dst_h = tex_max_h;
                imageData->tex_dst_w = imageData->tex_dst_h * aspect_ratio;
            }
            imageData->tex_dst_x = (tex_max_w - imageData->tex_dst_w) / 2;
            imageData->tex_dst_y = (tex_max_h - imageData->tex_dst_h) / 2;

            break;

        case TYPE_IMAGE_SCALE_MATRIX: // 图像原始大小，左上对齐
        default:
            imageData->tex_dst_w = tex_ori_w;
            imageData->tex_dst_h = tex_ori_h;
            imageData->tex_dst_x = 0;
            imageData->tex_dst_y = 0;

            break;
        }
    }

    return 0;
}

int ImageViewDraw(ImageView *imageView)
{
    if (!imageView || !imageView->viewdata)
        return -1;

    ImageViewData *imageData = (ImageViewData *)imageView->viewdata;
    LayoutParams *imageParams = &imageView->params;

    if (imageView->measured_w <= 0 || imageView->measured_h <= 0)
        return 0;

    int layout_x = imageParams->layout_x + imageParams->margin_left;
    int layout_y = imageParams->layout_y + imageParams->margin_top;
    int layout_w = imageView->measured_w;
    int layout_h = imageView->measured_h;

    int content_x = layout_x + imageView->padding_left;
    int content_y = layout_y + imageView->padding_top;
    int content_w = layout_w - imageView->padding_left - imageView->padding_right;
    int content_h = layout_h - imageView->padding_top - imageView->padding_bottom;

    if (imageView->bg_color)
        GUI_DrawFillRectangle(layout_x, layout_y, layout_w, layout_h, imageView->bg_color);

    if (imageData->tex)
    {
        GUI_SetClipping(content_x, content_y, content_w, content_h);

        int tex_dst_x = content_x + imageData->tex_dst_x;
        int tex_dst_y = content_y + imageData->tex_dst_y;
        float tex_scale_x = (float)imageData->tex_dst_w / imageData->tex_ori_w;
        float tex_scale_y = (float)imageData->tex_dst_h / imageData->tex_ori_h;

        if (imageData->tint_color)
            GUI_DrawTextureTintPartScale(imageData->tex, tex_dst_x, tex_dst_y, 0, 0,
                                         imageData->tex_ori_w, imageData->tex_ori_h, tex_scale_x, tex_scale_y, imageData->tint_color);
        else
            GUI_DrawTexturePartScale(imageData->tex, tex_dst_x, tex_dst_y, 0, 0,
                                     imageData->tex_ori_w, imageData->tex_ori_h, tex_scale_x, tex_scale_y);

        GUI_UnsetClipping();
    }

    return 0;
}

int ImageViewSetScaleType(ImageView *imageView, int type)
{
    if (!imageView || !imageView->viewdata)
        return -1;

    ImageViewData *imageData = (ImageViewData *)imageView->viewdata;
    imageData->scale_type = type;
    return 0;
}

int ImageViewGetScaleType(ImageView *imageView)
{
    if (!imageView || !imageView->viewdata)
        return 0;

    ImageViewData *imageData = (ImageViewData *)imageView->viewdata;
    return imageData->scale_type;
}

int ImageViewSetTintColor(ImageView *imageView, uint32_t color)
{
    if (!imageView || !imageView->viewdata)
        return -1;

    ImageViewData *imageData = (ImageViewData *)imageView->viewdata;
    imageData->tint_color = color;
    return 0;
}

uint32_t ImageViewGetTintColor(ImageView *imageView)
{
    if (!imageView || !imageView->viewdata)
        return 0;

    ImageViewData *imageData = (ImageViewData *)imageView->viewdata;
    return imageData->tint_color;
}

int ImageViewSetTexture(ImageView *imageView, const GUI_Texture *texture)
{
    if (!imageView || !imageView->viewdata)
        return -1;

    ImageViewData *imageData = (ImageViewData *)imageView->viewdata;
    imageData->tex = texture;
    imageData->tex_ori_w = 0;
    imageData->tex_ori_h = 0;

    if (imageData->tex)
    {
        imageData->tex_ori_w = GUI_GetTextureWidth(imageData->tex);
        imageData->tex_ori_h = GUI_GetTextureHeight(imageData->tex);
    }

    return 0;
}

const GUI_Texture *ImageViewGetTexture(ImageView *imageView)
{
    if (!imageView || !imageView->viewdata)
        return NULL;

    ImageViewData *imageData = (ImageViewData *)imageView->viewdata;
    return imageData->tex;
}

int ImageViewInit(ImageView *imageView)
{
    if (!imageView)
        return -1;

    memset(imageView, 0, sizeof(ImageView));

    imageView->viewdata = calloc(1, sizeof(ImageViewData));
    if (!imageView->viewdata)
        return -1;

    imageView->onMeasure = ImageViewMeasure;
    imageView->onDraw = ImageViewDraw;
    imageView->onFinish = ImageViewDestroy;

    return 0;
}

int ImageViewDeinit(ImageView *imageView)
{
    if (!imageView)
        return -1;

    ImageViewDestroyData((ImageViewData *)imageView->viewdata);
    memset(imageView, 0, sizeof(ImageView));

    return 0;
}

ImageView *NewImageView()
{
    ImageView *imageView = (ImageView *)malloc(sizeof(ImageView));
    if (!imageView)
        return NULL;

    if (ImageViewInit(imageView) < 0)
    {
        free(imageView);
        return NULL;
    }

    return imageView;
}

int ImageViewDestroy(ImageView *imageView)
{
    if (!imageView)
        return -1;

    if (imageView->dont_free)
        return 0;

    ImageViewDestroyData((ImageViewData *)imageView->viewdata);
    free(imageView);

    return 0;
}
